package ija.model.basis;

import ija.model.types.FigureColor;
import ija.model.types.FigureRoute;
import ija.model.types.GameMoves;
import ija.model.types.Tree;
import ija.model.types.Tree.Node;
import ija.xls.notation.DeskCoordinate;
import ija.xls.notation.types.GameOperation;

import java.io.Serializable;

/**
 * Abstraktní třída reprezentuje obecný typ hrací figurky.
 * 
 * @author Radek Necas, xnecas18
 * @see ija.model.figures.CheckersFigure, ija.model.figures.PieceGoForward
 * @see ija.model.figures.PieceGoBack, ija.model.figures.Queen
 */
public abstract class Figure implements Serializable {
    protected Position position;
    protected FigureColor color;

    /**
     * Konstruktor figurky.
     * 
     * @param p
     *            pozice figurky
     * @param c
     *            barva figurky
     */
    public Figure(Position p, FigureColor c) {
        this.color = c;
        this.position = p;
        this.position.putFigure(this);
    }

    /**
     * Konstruktor figurky pomoci figurky z jine sachovnice
     * 
     * @param p
     *            pozice figurky na aktualni sachovnici
     * @param copyFigure
     *            figurka, ze ktere se budou kopirovat udaje
     */
    public Figure(Position p, Figure copyFigure) {
        this.color = copyFigure.getColor();
        this.position = p;
        this.position.putFigure(this);
    }

    /**
     * 
     * @return pozice figurky.
     */
    public Position getPosition() {
        return this.position;
    }

    /**
     * 
     * @return barvu figurky
     */
    public FigureColor getColor() {
        return this.color;
    }

    /**
     * Metoda testuje, zda je figurka na uvedené pozici. Pozice nemusí být
     * součástí téže šachovnice.
     * 
     * @param p
     *            testovaná pozice.
     * @return true kladný výsledek, jinak false
     */
    public boolean isAtPosition(Position p) {
        return (this.position.equals(p));
    }

    /**
     * Metoda testuje, zda má figurka uvedenou barvu.
     * 
     * @param c
     *            true ano, false ne.
     * @return
     */
    public boolean hasColor(FigureColor c) {
        return (this.color == c);
    }

    /**
     * Metoda ruší pozici figurky. Nastavuje odkaz na pozici na null.
     * Odkazovanou pozici v šachovnici ale nemění (její odkaz na figurku).
     */
    public void deletePosition() {
        this.position = null;
    }

    /**
     * Metoda ozivi figurku. Pokud je figurka vyhozena, stale si pamatuje ze
     * ktere pozice ji souper vyhodil. Tato metoda ji na danou pozici zpet
     * vrati.
     * 
     * @return figurka, ktera byla na pozici, kam se figurka vracela nebo null,
     *         pokud tam zadna nebyla.
     */
    public Figure revive() {
        if (this.position != null) {
            return this.position.putFigure(this);
        }

        return null;
    }

    /**
     * Metod posune figurku na uvedenou pozici. Mění všechny související pozice
     * (zdrojovou, cílovou) odpovídajícím způsobem. Pohyb provede pouze pokud je
     * povolený dle typu figurky.
     * 
     * @param p
     *            pozice kam se má figurka posunout. Měla by být součástí téže
     *            šachovnice (ale není testováno).
     * @return true pokud je figurka posunuta, jinak false.
     */
    public boolean move(Position p) {
        if (this.canMove(p)) {
            this.position.removeFigure();
            // position = p;
            p.putFigure(this);
            return true;
        }

        return false;
    }

    /**
     * Metoda posune figurku na danou pozici. Neprovádí při tom žádné testy.
     * 
     * @param p
     *            pozice kam se figurka vloží.
     */
    public void uncheckedMove(Position p) {
        p.figure = this;
        this.position.figure = null;
        this.position = p;
    }

    @Override
    public String toString() {
        String s = "Trida: " + this.getClass().getName() + " - Barva: ";
        s += (this.color == FigureColor.BLACK ? "BLACK" : "WHITE");
        return s;
    }

    /**
     * Metoda vraci všechny validní pohyby figurky. Pokud figurka může skákat
     * vrací pouze skoky. Víceskoky nejsou upřednostněny před běžnými skoky.
     * Pokud není skok možný vrátí se běžné pohyby.
     * 
     * @return všechny validní pohyby figurky.
     */
    public GameMoves getAllMoves() {
        Tree<FigureRoute> positions = new Tree(new FigureRoute(new DeskCoordinate(this.position),
                this.position.toString()));
        if (this.getJumps(positions.getRoot())) {
            return new GameMoves(positions, GameOperation.JUMP);
        }

        else if (this.getMoves(positions.getRoot())) {
            return new GameMoves(positions, GameOperation.MOVE);
        }

        return null;
    }

    // public abstract GameMoves getAllMoves();
    public abstract boolean canMove(Position p);

    public abstract boolean inDirection(Position p);

    public abstract boolean getJumps(Node<FigureRoute> root);

    public abstract boolean getMoves(Node<FigureRoute> root);
}